package com.mav01.mrpbackend.service.impl;

import com.mav01.mrpbackend.mapper.*;
import com.mav01.mrpbackend.pojo.*;
import com.mav01.mrpbackend.service.MrpCalculationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class MrpCalculationServiceImpl implements MrpCalculationService {

    @Autowired
    private MrpPlanMapper mrpPlanMapper;

    @Autowired
    private MaterialMapper materialMapper;

    @Autowired
    private AllocationMapper allocationMapper;

    @Autowired
    private StoreMapper storeMapper;

    /**
     * 执行MRP计算
     */
    public MrpPlan calculateMrp(MrpPlanRequest request) {
        // 1. 创建计划记录
        MrpPlan plan = createMrpPlan(request);

        // 2. 执行MRP计算
        List<MrpResult> results = performMrpCalculation(request);

        // 3. 保存计算结果
        saveMrpResults(plan.getPlanId(), results);

        // 4. 设置结果并返回
        plan.setResults(results);
        return plan;
    }

    private MrpPlan createMrpPlan(MrpPlanRequest request) {
        MrpPlan plan = new MrpPlan();
        plan.setPlanName(request.getPlanName());
        plan.setProductMaterialNo(request.getProductMaterialNo());
        plan.setProductName(getProductName(request.getProductMaterialNo()));
        plan.setProductionQuantity(request.getProductionQuantity());
        plan.setDeliveryDate(request.getDeliveryDate());

        mrpPlanMapper.insertMrpPlan(plan);
        return plan;
    }

    private List<MrpResult> performMrpCalculation(MrpPlanRequest request) {
        // 为每次计算创建独立的库存副本
        Map<String, Store> calculationInventory = createInventoryCopy();

        List<MrpResult> results = new ArrayList<>();
        String productMaterialNo = request.getProductMaterialNo();
        Integer productionQuantity = request.getProductionQuantity();
        Date deliveryDate = request.getDeliveryDate();

        // 先计算所有物料的毛需求（不应用库存）
        List<MrpNode> allNodes = calculateAllNodes(productMaterialNo, productionQuantity, deliveryDate);

        // 按照层级分组：层级2的先处理，然后是层级1
        Map<Integer, List<MrpNode>> nodesByLevel = allNodes.stream()
                .collect(Collectors.groupingBy(MrpNode::getLevel));

        // 先处理层级2的节点
        if (nodesByLevel.containsKey(2)) {
            for (MrpNode node : nodesByLevel.get(2)) {
                MrpResult result = calculateNodeWithInventory(node, calculationInventory);
                if (result != null && result.getQuantity() > 0) {
                    results.add(result);
                }
            }
        }

        // 再处理层级1的节点
        if (nodesByLevel.containsKey(1)) {
            for (MrpNode node : nodesByLevel.get(1)) {
                MrpResult result = calculateNodeWithInventory(node, calculationInventory);
                if (result != null && result.getQuantity() > 0) {
                    results.add(result);
                }
            }
        }

        // 最后处理层级0的节点（产品本身）
        if (nodesByLevel.containsKey(0)) {
            for (MrpNode node : nodesByLevel.get(0)) {
                MrpResult result = calculateNode(node); // 产品不需要应用库存
                if (result != null) {
                    results.add(result);
                }
            }
        }

        return results;
    }

    /**
     * 创建库存副本
     */
    private Map<String, Store> createInventoryCopy() {
        Map<String, Store> inventoryCopy = new HashMap<>();
        List<Store> stores = storeMapper.selectAll();
        for (Store store : stores) {
            // 创建Store的深拷贝
            Store copy = new Store();
            copy.setMaterialNo(store.getMaterialNo());
            copy.setMaterialName(store.getMaterialName());
            copy.setProcessInventory(store.getProcessInventory());
            copy.setMaterialInventory(store.getMaterialInventory());
            inventoryCopy.put(store.getMaterialNo(), copy);
        }
        return inventoryCopy;
    }

    /**
     * 计算所有节点的毛需求
     */
    private List<MrpNode> calculateAllNodes(String productMaterialNo, Integer productionQuantity, Date deliveryDate) {
        List<MrpNode> allNodes = new ArrayList<>();

        // 计算产品本身（层级0）
        MrpNode productNode = calculateProductNode(productMaterialNo, productionQuantity, deliveryDate);
        allNodes.add(productNode);

        // 计算第一层子节点
        List<Allocation> level1Allocations = allocationMapper.selectByParentMaterialNo(productMaterialNo);
        for (Allocation allocation : level1Allocations) {
            MrpNode level1Node = calculateChildNode(allocation, productNode, 1);
            allNodes.add(level1Node);

            // 如果第一层节点是生产件，计算其第二层子节点
            if ("生产".equals(getProductionMethod(allocation.getChildMaterialNo()))) {
                List<Allocation> level2Allocations = allocationMapper.selectByParentMaterialNo(allocation.getChildMaterialNo());
                for (Allocation level2Allocation : level2Allocations) {
                    MrpNode level2Node = calculateChildNode(level2Allocation, level1Node, 2);
                    allNodes.add(level2Node);
                }
            }
        }

        return allNodes;
    }

    /**
     * 计算产品节点
     */
    private MrpNode calculateProductNode(String productMaterialNo, Integer productionQuantity, Date deliveryDate) {
        Material productMaterial = materialMapper.selectByMaterialNo(productMaterialNo);

        // 产品完成日期 = 提货日期
        Date completionDate = deliveryDate;

        // 产品下达日期 = 完成日期 - 作业提前期
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(completionDate);
        calendar.add(Calendar.DAY_OF_MONTH, -productMaterial.getOperationLeadTime());
        Date orderDate = calendar.getTime();

        return new MrpNode(productMaterialNo, productionQuantity, completionDate, orderDate, 0, null);
    }

    /**
     * 计算子节点
     */
    private MrpNode calculateChildNode(Allocation allocation, MrpNode parentNode, int level) {
        String childMaterialNo = allocation.getChildMaterialNo();
        Material childMaterial = materialMapper.selectByMaterialNo(childMaterialNo);

        // 1. 计算需求数量（毛需求）
        Integer grossQuantity = calculateGrossQuantity(parentNode.getQuantity(),
                allocation.getCompositionQuantity(),
                childMaterialNo);

        // 2. 子物料的日程完成日期 = 父物料的日程下达日期
        Date completionDate = parentNode.getOrderDate();

        // 3. 子物料的日程下达日期 = 子物料的日程完成日期 - 子物料作业提前期 - 子物料配料提前期 - 子物料供应商提前期
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(completionDate);

        int totalLeadTime = childMaterial.getOperationLeadTime() +
                allocation.getIngredientLeadTime() +
                allocation.getSupplierLeadTime();

        calendar.add(Calendar.DAY_OF_MONTH, -totalLeadTime);
        Date orderDate = calendar.getTime();

        return new MrpNode(childMaterialNo, grossQuantity, completionDate, orderDate, level, parentNode.getMaterialNo());
    }

    /**
     * 计算毛需求数量 = (父物料需求数 * 子物料构成数) / (1 - 损耗率)
     */
    private Integer calculateGrossQuantity(Integer parentQuantity, Integer compositionQuantity, String materialNo) {
        Integer baseQuantity = parentQuantity * compositionQuantity;

        Material material = materialMapper.selectByMaterialNo(materialNo);
        if (material != null && material.getScrapRate() > 0) {
            // 考虑损耗率：实际需求 = 基础需求 / (1 - 损耗率)
            double scrapRate = material.getScrapRate();
            return (int) Math.ceil(baseQuantity / (1 - scrapRate));
        }

        return baseQuantity;
    }

    /**
     * 计算节点并应用库存
     */
    private MrpResult calculateNodeWithInventory(MrpNode node, Map<String, Store> inventory) {
        Material material = materialMapper.selectByMaterialNo(node.getMaterialNo());
        if (material == null) {
            return null;
        }

        // 应用库存计算净需求
        Integer netQuantity = applyInventory(node.getMaterialNo(), node.getQuantity(), inventory);

        if (netQuantity <= 0) {
            return null;
        }

        MrpResult result = new MrpResult();
        result.setMaterialNo(node.getMaterialNo());
        result.setMaterialName(material.getName());
        result.setProductionMethod(material.getAllocationMethod());
        result.setQuantity(netQuantity);
        result.setLevel(node.getLevel());
        result.setParentMaterialNo(node.getParentMaterialNo());
        result.setOrderDate(node.getOrderDate());
        result.setCompletionDate(node.getCompletionDate());

        return result;
    }

    /**
     * 计算节点（不应用库存，用于产品本身）
     */
    private MrpResult calculateNode(MrpNode node) {
        Material material = materialMapper.selectByMaterialNo(node.getMaterialNo());
        if (material == null) {
            return null;
        }

        MrpResult result = new MrpResult();
        result.setMaterialNo(node.getMaterialNo());
        result.setMaterialName(material.getName());
        result.setProductionMethod(material.getAllocationMethod());
        result.setQuantity(node.getQuantity());
        result.setLevel(node.getLevel());
        result.setParentMaterialNo(node.getParentMaterialNo());
        result.setOrderDate(node.getOrderDate());
        result.setCompletionDate(node.getCompletionDate());

        return result;
    }

    /**
     * 应用库存，返回净需求数量 = 毛需求 - 工序库存量 - 资材库存量
     */
    private Integer applyInventory(String materialNo, Integer grossQuantity, Map<String, Store> inventory) {
        Store store = inventory.get(materialNo);
        if (store == null) {
            return grossQuantity;
        }

        int availableProcess = store.getProcessInventory();
        int availableMaterial = store.getMaterialInventory();
        int totalAvailable = availableProcess + availableMaterial;

        if (totalAvailable >= grossQuantity) {
            // 库存充足，全部使用
            // 先使用工序库存，再使用资材库存
            int remaining = grossQuantity;

            if (availableProcess >= remaining) {
                store.setProcessInventory(availableProcess - remaining);
            } else {
                remaining -= availableProcess;
                store.setProcessInventory(0);
                store.setMaterialInventory(availableMaterial - remaining);
            }

            return 0; // 净需求为0
        } else {
            // 库存不足，使用全部库存
            Integer netQuantity = grossQuantity - totalAvailable;
            store.setProcessInventory(0);
            store.setMaterialInventory(0);
            return netQuantity;
        }
    }

    private String getProductionMethod(String materialNo) {
        Material material = materialMapper.selectByMaterialNo(materialNo);
        return material != null ? material.getAllocationMethod() : "采购";
    }

    private String getProductName(String materialNo) {
        Material material = materialMapper.selectByMaterialNo(materialNo);
        return material != null ? material.getName() : materialNo;
    }

    private void saveMrpResults(Long planId, List<MrpResult> results) {
        for (MrpResult result : results) {
            result.setPlanId(planId);
        }
        mrpPlanMapper.batchInsertMrpResults(results);
    }
}
